#include "../stdlib.hpp"
#include <purewell/code/hexa.hpp>

namespace purewell
{

PWHexa& gPWHexa(PWHexa::getInstance());

inline
static
unsigned char
_toHexHalf(unsigned char c, bool cap)
{
	if ( c < 10 )
	{
		return ('0'+c);
	}

	return ((cap?'A':'a') + c - 10);
}

inline
static
unsigned char
_toBinHalf(unsigned char i)
{
	if ( i >= (unsigned char)'0' && i <= (unsigned char)'9' )
	{
		return i-(unsigned char)'0';
	}

	if ( i >= (unsigned char)'A' && i <= (unsigned char)'Z' )
	{
		return i - (unsigned char)'A' + 10;
	}

	return i - (unsigned char)'a' + 10;
}

inline
static
bool
_isHex(unsigned char c)
{
	if ( (unsigned char)'0' >= c && (unsigned char)'9' <= c ) return true;
	if ( (unsigned char)'A' >= c && (unsigned char)'F' <= c ) return true;
	if ( (unsigned char)'a' >= c && (unsigned char)'f' <= c ) return true;
	return false;
}

PWHexa::PWHexa()
{
}

PWHexa&
PWHexa::getInstance(void)
{
	static PWHexa inst;
	return inst;
}

char*
PWHexa::encode(char o[2], char c, bool cap) const
{
	o[0] = (char)_toHexHalf(c>>4, cap);
	o[1] = (char)_toHexHalf(c&0x0f, cap);
	return o;
}

char
PWHexa::decode(const char o[2]) const
{
	return (char)( _toBinHalf(o[0]) << 4 | _toBinHalf(o[1]) );
}

std::ostream&
PWHexa::encode(std::ostream& os, std::istream& is, bool cap) const
{
	char c;
	char o[2];

	while ( !is.eof() )
	{
		is.get(c);
		if ( !is.good() ) break;
		os.write(encode(o, c, cap), 2);
	}

	return os;
}

size_t
PWHexa::encode(char* obuf, const char* ibuf, size_t iblen, bool cap) const
{
	const char* ib(ibuf);
	const char* ie(ibuf+iblen);
	size_t ret(0);
	char o[2];

	while ( ib != ie )
	{
		encode(o, *ib, cap);
		*(obuf+ret) = o[0];
		*(obuf+ret+1) = o[1];
		ret += 2;
		++ib;
	}

	return ret;
}

std::ostream&
PWHexa::decode(std::ostream& os, std::istream& is) const
{
	char i[2];

	while ( !is.eof() )
	{
		is.get(i[0]);
		if ( !is.good() ) break;

		is.get(i[1]);
		if ( !is.good() )
		{
			i[1] = 0x00;
		}

		os << decode(i);
	}

	return os;
}

size_t
PWHexa::decode(char* obuf, const char* ibuf, size_t iblen) const
{
	const char* ib(ibuf);
	const char* ie(ibuf+iblen);
	char i[2];
	size_t ret(0);

	while ( ib != ie )
	{
		i[0] = *ib;
		if ( ib+1 == ie )
		{
			i[1] = 0x00;
			++ib;
		}
		else
		{
			i[1] = *(ib+1);
			ib += 2;
		}

		*(obuf+ret) = decode(i);

		++ret;
	}

	return ret;
}

};//namespace purewell


